//////////////////////////////////////////////////////////////////////////
#pragma once


//////////////////////////////////////////////////////////////////////////
#include "Math/Math.hpp"
#include "Math/Simd/Simd.hpp"


namespace Math
{
	// NOTE: Currently only enough support to allow Matrix44fv to be based
	// on this type.
	template< typename Simd=Simd_t >
	class BEGIN_ALIGNED( Vector4f_Simd, 16 )
	{
	public:
		// Pull the type from the base Simd definition.
		typedef typename Simd::F32x4_t									ValueType_t;
		typedef typename Math::Simd::Param< ValueType_t >::Type_t		ParamType_t;

		// Element index constants.
		static const size_t						kXIndex		=	0;
		static const size_t						kYIndex		=	1;
		static const size_t						kZIndex		=	2;
		static const size_t						kWIndex		=	3;

		// Construction.
		Vector4f_Simd()																				{}
		Vector4f_Simd( const Vector4f_Simd& rhs ) : mData( rhs.mData )								{}
		Vector4f_Simd( float x, float y, float z, float w ) : mData( Simd::Create( x, y, z, w ) )	{}

		Vector4f_Simd( ValueType_t rhs ): mData( rhs )												{}
		operator ValueType_t() const																{return mData;}

		float						X() const														{return Simd::Fetch( mData, 0 );}
		float						Y() const														{return Simd::Fetch( mData, 1 );}
		float						Z() const														{return Simd::Fetch( mData, 2 );}
		float						W() const														{return Simd::Fetch( mData, 3 );}

		void						X( float x )													{mData = Simd::Store( mData, x, 0 );}
		void						Y( float y )													{mData = Simd::Store( mData, y, 1 );}
		void						Z( float z )													{mData = Simd::Store( mData, z, 2 );}
		void						W( float w )													{mData = Simd::Store( mData, w, 3 );}

		float						operator ()( size_t index ) const								{return Simd::Fetch( mData, index );}
		void						operator ()( size_t index, float v )							{mData = Simd::Store( mData, v, index );}

		// Explicit set operation.  Syntactic sugar for X(x); Y(y); Z(z) W(w), also a bit faster.
		void						Set( float x, float y, float z, float w )						{mData = Simd::Create( x, y, z, w );}

		// Assignment.
		const Vector4f_Simd&		operator =( const Vector4f_Simd& v )							{mData = v.mData; return *this;}

		//
		const Vector4f_Simd			operator +( const Vector4f_Simd& v ) const						{return Simd::Add( mData, v );}
		const Vector4f_Simd			operator *( float rhs ) const									{return Simd::Mul( mData, Simd::Create( rhs, rhs, rhs ) );}
		void						operator +=( const Vector4f_Simd& v )							{mData = Simd::Add( mData, v );}
		void						operator /=( float rhs )										{mData = Simd::Div( mData, Simd::Create( rhs, rhs, rhs, 1.0f ) );}
		void						operator *=( float rhs )										{mData = Simd::Mul( mData, Simd::Create( rhs, rhs, rhs ) );}

		// Static initializers.
		static Vector4f_Simd		Zero()															{return Vector4f_Simd( 0.0f, 0.0f, 0.0f, 0.0f );}
		static Vector4f_Simd		XAxis()															{return Vector4f_Simd( 1.0f, 0.0f, 0.0f, 0.0f );}
		static Vector4f_Simd		YAxis()															{return Vector4f_Simd( 0.0f, 1.0f, 0.0f, 0.0f );}
		static Vector4f_Simd		ZAxis()															{return Vector4f_Simd( 0.0f, 0.0f, 1.0f, 0.0f );}
		static Vector4f_Simd		WAxis()															{return Vector4f_Simd( 0.0f, 0.0f, 0.0f, 1.0f );}

	private:
		// Implementation data.
		ValueType_t					mData;
	} END_ALIGNED( Vector3f_Simd, 16 );

	typedef Vector4f_Simd< Simd_t >			Vector4fv;


	//////////////////////////////////////////////////////////////////////////
	// Not really a vector thing, used for color processing at the moment.
	template< typename Type >
	inline Vector4f_Simd< Type >	Multiply( const Vector4f_Simd< Type >& lhs, const Vector4f_Simd< Type >& rhs )
	{
		return( Type::Mul( lhs, rhs ) );
	}
}
